/***********************************************************************
**
**  REBOL [R3] Language Interpreter and Run-time Environment
**
**  Copyright 2012 REBOL Technologies
**  REBOL is a trademark of REBOL Technologies
**
**  Licensed under the Apache License, Version 2.0 (the "License");
**  you may not use this file except in compliance with the License.
**  You may obtain a copy of the License at
**
**  http://www.apache.org/licenses/LICENSE-2.0
**
**  Unless required by applicable law or agreed to in writing, software
**  distributed under the License is distributed on an "AS IS" BASIS,
**  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
**  See the License for the specific language governing permissions and
**  limitations under the License.
**
************************************************************************
**
**  Title: Effect Dialect Backend
**  Author: Cyphre, Carl
**  Purpose: Evaluates EFFECT commands; calls graphics functions.
**
************************************************************************
**
**  NOTE to PROGRAMMERS:
**
**    1. Keep code clear and simple.
**    2. Document unusual code, reasoning, or gotchas.
**    3. Use same style for code, vars, indent(4), comments, etc.
**    4. Keep in mind Linux, OS X, BSD, big/little endian CPUs.
**    5. Test everything, then test it again.
**
***********************************************************************/

#include <stdlib.h>

#include "reb-host.h"
///#include "reb-series.h"
///#include "reb-gob.h"
#include "host-lib.h"
#include "reb-types.h"
#include "reb-value.h"
#include "reb-dialect.h"
#include "words-effect.h"	// Auto-generated by gen-effect-words.r
#include "rebol-lib.h"

#include "agg-effects.h"

//#define AGGC ((agg_graphics*)context)

#define ARG_LOGIC(n)	VAL_LOGIC(arg+n)
#define ARG_INTEGER(n)	VAL_INT32(arg+n)
#define ARG_STRING(n)	VAL_STRING(arg+n)
#define ARG_PAIR(n)		VAL_PAIR(arg+n)
#define ARG_DECIMAL(n)	VAL_DECIMAL(arg+n)
#define ARG_TUPLE(n)	VAL_TUPLE(arg+n)
#define ARG_WORD(n)		VAL_WORD(arg+n)

#define ARG_OBJECT(n)	VAL_SERIES(arg+n)  // temp
#define ARG_IMAGE(n)	VAL_SERIES(arg+n)  // temp
#define ARG_BLOCK(n)	VAL_SERIES(arg+n)

#define ARG_OPT_IMAGE(n) (IS_IMAGE(arg+n)? ARG_IMAGE(n) : NULL)

#define ARG_WORDS(n,s,e) ((ARG_WORD(n)>=s && ARG_WORD(n)<=e) ? ARG_WORD(n)-s : 0)



/***********************************************************************
**
*/	REBINT Effect_Gob(void *effects, REBSER *block)
/*
**		Handles all commands for the EFFECT dialect as specified
**		in the system/dialects/effect object.
**
**		This function calls the REBOL_Dialect interpreter to
**		parse the dialect and build and return the command number
**		(the index offset in the draw object above) and a block
**		of arguments. (For now, just a REBOL block, but this could
**		be changed to isolate it from changes in REBOL's internals).
**
**		Each arg will be of the specified datatype (given in the
**		dialect) or NONE when no argument of that type was given
**		and this code must determine the proper default value.
**
**		If the cmd result is zero, then it is either the end of
**		the block, or an error has occurred. If the error value
**		is non-zero, then it was an error.
**
***********************************************************************/
{
//	REBSER *block;
	REBCNT index = 0;
	REBINT cmd;
	REBSER *args = 0;
	REBVAL *arg;
	REBCNT nargs;

	//default values
	REBYTE def_color1[4] = {0,0,0,0};
	REBYTE def_color2[4] = {255,255,255,0};
	REBPAR def_pair = {1,0};

	do {
		cmd = Reb_Dialect(DIALECTS_EFFECT, block, &index, &args);

		if (cmd == 0) return 0;
		if (cmd < 0) {
//			Reb_Print("ERROR: %d, Index %d", -cmd, index);
			return -((REBINT)index+1);
		}
//		else
//			Reb_Print("EFFECT: Cmd %d, Index %d, Args %m", cmd, index, args);

		arg = BLK_HEAD(args);
		nargs = SERIES_TAIL(args);
//		Reb_Print("Number of args: %d", nargs);

		switch (cmd) {

		case EW_ADD:
			FX_Add(effects, ARG_OPT_IMAGE(0), ARG_OPT_IMAGE(1));
			break;
		case EW_ALPHAMUL:
			if (IS_IMAGE(arg))
				FX_Alphamul(effects, ARG_IMAGE(0), IS_NONE(arg+1) ? 127 : ARG_INTEGER(1));
			break;
		case EW_ASPECT:
			{
				REBINT type = 1, mode = 2;

				if (ARG_WORD(0) == EW_RESAMPLE){
						type = 2;
						mode = 1;
				}

				FX_Fit(effects,ARG_OPT_IMAGE(0), ARG_WORDS(type,EW_NEAREST,EW_GAUSSIAN), ARG_WORD(mode) == EW_RESAMPLE, IS_NONE(arg+3) ? 1.0 : ARG_DECIMAL(3), TRUE);
			}
			break;
		case EW_BLUR:
//			FX_Blur(effects, ARG_OPT_IMAGE(0));
			{
				REBDEC filter[9] = {0, 1, 0, 1, 1, 1, 0, 1, 0};
				FX_Convolve(effects, ARG_OPT_IMAGE(0),filter , 5.0, 0, FALSE);
			}
			break;
		case EW_COLORIFY:
			FX_Colorify(effects, IS_NONE(arg+1) ? def_color2 : ARG_TUPLE(1) , IS_NONE(arg+2) ? 255 : max(0, min(255,ARG_INTEGER(2))), ARG_OPT_IMAGE(0));
			break;
		case EW_COLORIZE:
			FX_Colorize(effects, IS_NONE(arg+1) ? def_color2 : ARG_TUPLE(1) , ARG_OPT_IMAGE(0));
			break;
		case EW_CONTRAST:
			FX_Contrast(effects, IS_NONE(arg+1) ? 127 : ARG_INTEGER(1), ARG_OPT_IMAGE(0));
			break;
		case EW_CONVOLVE:
			//[image! block! decimal! decimal! logic!]
			if (IS_BLOCK(arg+1)) {
				REBDEC filter[9];
				REBSER* mtx = (REBSER*)ARG_BLOCK(1);
				REBVAL* slot = BLK_HEAD(mtx);
				REBCNT len = SERIES_TAIL(mtx) ,i, num = 0;

				for (i = 0;i<len;i++){
					if (IS_DECIMAL(slot+i))
						filter[i] = VAL_DECIMAL(slot+i);
					else if (IS_INTEGER(slot+i))
						filter[i] = VAL_INT32(slot+i);
					else
						return -cmd;
					num++;
				}

				if (num != 9) return -cmd;

				FX_Convolve(effects, ARG_OPT_IMAGE(0),filter , ARG_DECIMAL(2), ARG_INTEGER(3), ARG_LOGIC(4));
			}
			break;
		case EW_CROP:
			FX_Crop(effects,ARG_OPT_IMAGE(0), IS_NONE(arg+1) ? 0 : &ARG_PAIR(1), IS_NONE(arg+2) ? 0 : &ARG_PAIR(2));
			break;
		case EW_DIFFERENCE:
			FX_Difference(effects, IS_NONE(arg+2) ? def_color2 : ARG_TUPLE(2), ARG_OPT_IMAGE(0), ARG_OPT_IMAGE(1), (IS_NONE(arg+2)) ? 1 : 0);
			break;

		case EW_EMBOSS:
			{
				REBDEC filter[9] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
				FX_Convolve(effects, ARG_OPT_IMAGE(0),filter , 6.0, 127, FALSE);
			}
			break;

		case EW_EXTEND:
			FX_Extend(effects,ARG_OPT_IMAGE(0), IS_NONE(arg+1) ? 0 : &ARG_PAIR(1), IS_NONE(arg+2) ? 0 : &ARG_PAIR(2));
			break;

		case EW_FIT:
			if (IS_IMAGE(arg)){
				REBINT type = 1, mode = 2;
				if (ARG_WORD(0) == EW_RESAMPLE){
					type = 2;
					mode = 1;
				}

				FX_Fit(effects,ARG_IMAGE(0), ARG_WORDS(type,EW_NEAREST,EW_GAUSSIAN), ARG_WORD(mode) == EW_RESAMPLE, IS_NONE(arg+3) ? 1.0 : ARG_DECIMAL(3), FALSE);
			}
			break;

		case EW_FLIP:
			FX_Flip(effects, IS_NONE(arg+1) ? &def_pair : &ARG_PAIR(1), ARG_OPT_IMAGE(0));
			break;

		case EW_GRADCOL:
			FX_Gradcol(effects, &ARG_PAIR(1), IS_NONE(arg+2) ? def_color1 : ARG_TUPLE(2), IS_NONE(arg+3) ? def_color2 : ARG_TUPLE(3),ARG_OPT_IMAGE(0));
			break;

		case EW_GRADIENT:
			FX_Gradient(effects, &ARG_PAIR(1), IS_NONE(arg+2) ? def_color1 : ARG_TUPLE(2), IS_NONE(arg+3) ? def_color2 : ARG_TUPLE(3), ARG_OPT_IMAGE(0));
			break;

		case EW_GRADMUL:
			FX_Gradmul(effects, &ARG_PAIR(1), IS_NONE(arg+2) ? def_color1 : ARG_TUPLE(2), IS_NONE(arg+3) ? def_color2 : ARG_TUPLE(3), ARG_OPT_IMAGE(0));
			break;

		case EW_GRAYSCALE:
			FX_Grayscale(effects,ARG_OPT_IMAGE(0));
			break;

		case EW_HSV:
			FX_HSV(effects, IS_NONE(arg+1) ? def_color2 : ARG_TUPLE(1), ARG_OPT_IMAGE(0));
			break;

		case EW_INVERT:
			FX_Invert(effects,ARG_OPT_IMAGE(0));
			break;
		case EW_KEY:
			if (IS_IMAGE(arg))
				FX_Key(effects, IS_NONE(arg+1) ? def_color1 : ARG_TUPLE(1), ARG_IMAGE(0));
			break;
		case EW_LUMA:
			FX_Luma(effects, IS_NONE(arg+1) ? 127 : ARG_INTEGER(1),ARG_OPT_IMAGE(0));
			break;

		case EW_MIX:
			FX_Mix(effects, ARG_OPT_IMAGE(0), ARG_OPT_IMAGE(1));
			break;

		case EW_MULTIPLY:
			{
				REBINT i = 0x00FFFFFF;
				REBYTE* color = (REBYTE*)&i;
				if (IS_INTEGER(arg+3)) {
					i = ARG_INTEGER(3);
					i = i + (i << 8) + (i << 16);
				} else if (IS_TUPLE(arg+2))
					color = ARG_TUPLE(2);
				FX_Multiply(effects, color ,ARG_OPT_IMAGE(0), ARG_OPT_IMAGE(1), (IS_NONE(arg+2) &&  IS_NONE(arg+3)) ? 1 : 0);
			}
			break;

		case EW_REFLECT:
			FX_Reflect(effects, IS_NONE(arg+1) ? &def_pair : &ARG_PAIR(1), ARG_OPT_IMAGE(0));
			break;

		case EW_ROTATE:
			FX_Rotate(effects, IS_NONE(arg+1) ? 90 : ARG_INTEGER(1), ARG_OPT_IMAGE(0));
			break;
		case EW_SHADOW:
			if (IS_IMAGE(arg))
				FX_Shadow(effects, ARG_IMAGE(0), IS_NONE(arg+1) ? 0 : &ARG_PAIR(1), IS_NONE(arg+2) ? 0 : &ARG_PAIR(2), IS_NONE(arg+3) ? 0 : ARG_TUPLE(3), IS_NONE(arg+4) ? 0 : ARG_DECIMAL(4), IS_NONE(arg+5) ? 0 : ARG_WORD(5) == EW_ONLY);
			break;
		case EW_SHARPEN:
//			FX_Sharpen(effects, ARG_OPT_IMAGE(0));
			{
				REBDEC filter[9] = {0, -1, 0, -1, 8, -1, 0, -1, 0};
				FX_Convolve(effects, ARG_OPT_IMAGE(0),filter , 4.0, 0, FALSE);
			}
			break;
		case EW_TILE:
			if (IS_IMAGE(arg))
				FX_Tile(effects, ARG_IMAGE(0), &ARG_PAIR(1));
			break;
		case EW_TILE_VIEW:
			if (IS_IMAGE(arg)) {
				REBPAR p;
				Effect_Offset(effects, &p);
				FX_Tile(effects, ARG_IMAGE(0), &p);
			}
			break;
		case EW_TINT:
			FX_Tint(effects, IS_NONE(arg+1) ? 127 : ARG_INTEGER(1),ARG_OPT_IMAGE(0));
			break;
		}
	} while (TRUE);
}
